package com.jlf.controller.engine;

import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.jlf.controller.engine.entity.field.SchemaFieldBatchInput;
import com.jlf.controller.engine.entity.field.SchemaFieldDetail;
import com.jlf.controller.engine.entity.field.SchemaFieldInput;
import com.jlf.core.annotation.restful.controller.RestfulController;
import com.jlf.core.annotation.restful.method.RestfulDelete;
import com.jlf.core.annotation.restful.method.RestfulGet;
import com.jlf.core.annotation.restful.method.RestfulPost;
import com.jlf.core.annotation.restful.method.RestfulPut;
import com.jlf.core.engine.domain.SchemaDomainDO;
import com.jlf.core.engine.domain.SchemaFieldDO;
import com.jlf.core.engine.domain.SchemaIndexDO;
import com.jlf.core.engine.entity.field.DeleteFieldWrapper;
import com.jlf.core.engine.entity.field.EditFieldWrapper;
import com.jlf.core.engine.persistence.SchemaDomainPersistence;
import com.jlf.core.engine.persistence.SchemaFieldPersistence;
import com.jlf.core.engine.persistence.SchemaIndexPersistence;
import com.jlf.core.engine.query.SchemaFieldQueryInput;
import com.jlf.core.engine.server.SchemaFieldServer;
import com.jlf.core.error.Errors;
import com.jlf.core.error.exception.NullReferenceException;
import com.jlf.core.output.ListResultWrapper;
import com.jlf.core.output.ResultException;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * 领域字段定义的接口控制器
 *
 * @author wujr
 * 2023/7/7
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/7 1.0 新增]
 */
@Slf4j
@Tag(name = "模型领域字段管理接口")
@RestfulController(path = "/rest/jlf/core/engine/schema/fields", name = "模型领域字段管理接口", domainClass = SchemaFieldDO.class)
@RequiredArgsConstructor
public class SchemaFieldController {
    private final SchemaFieldServer schemaFieldServer;
    private final SchemaFieldPersistence schemaFieldPersistence;
    private final SchemaDomainPersistence schemaDomainPersistence;
    private final SchemaIndexPersistence schemaIndexPersistence;

    @RestfulPost(code = "addSchemaField", name = "新增模型领域字段", path = "/domain/{domainId}")
    public SchemaFieldDO addSchemaField(@Valid @RequestBody SchemaFieldInput input,
                             @PathVariable("domainId") Long domainId) throws Exception{
        SchemaDomainDO domain = schemaDomainPersistence.getById(domainId);
        if (domain == null){
            log.info("新增模型领域字段，所属模型：{} 不存在", domainId);
            throw new NullReferenceException(SchemaFieldDO.class, "模型领域不存在");
        }
        SchemaFieldDO record = input.to();
        record.setDomainCode(domain.getDomainCode());
        return schemaFieldServer.add(record);
    }
    @RestfulPost(code = "batchAddSchemaField", name = "批量新增模型领域字段", path = "/domain")
    public void batchAddSchemaField(@Valid @RequestBody SchemaFieldBatchInput input) throws Exception{
        SchemaDomainDO domain = schemaDomainPersistence.getById(input.getDomainId());
        if (domain == null){
            log.info("批量新增模型领域字段，所属模型：{} 不存在", input.getDomainId());
            throw new NullReferenceException(SchemaFieldDO.class, "模型领域不存在");
        }
        List<SchemaFieldDO> records = new ArrayList<>();
        for (SchemaFieldInput field : input.getFields()){
            SchemaFieldDO record = field.to();
            record.setDomainCode(domain.getDomainCode());
            records.add(record);
        }
        schemaFieldServer.add(records);
    }

    @RestfulPut(code = "editSchemaField", name = "修改模型领域字段", path = "/{fieldId}")
    public SchemaFieldDO editSchemaField(@PathVariable("fieldId") Long fieldId,
                                         @Valid @RequestBody SchemaFieldInput input) throws Exception{
        SchemaFieldDO original = schemaFieldPersistence.getById(fieldId);
        if (original == null){
            log.info("修改模型领域字段：{}，该字段不存在", fieldId);
            throw new ResultException(Errors.NO_OBJECT, "模型字段不存在");
        }
        SchemaFieldDO record = input.to();
        EditFieldWrapper wrapper = new EditFieldWrapper();
        wrapper.setOriginal(original);
        wrapper.setRecord(record);
        wrapper.setSchemaFields(schemaFieldPersistence.loadByDomain(original.getDomainCode(), null));
        wrapper.setSchemaIndexes(schemaIndexPersistence.loadByDomain(original.getDomainCode()));
        // 统计该模型领域的数据表的记录数量
        SchemaDomainDO domain = schemaDomainPersistence.getByCode(original.getDomainCode());
        String sqlCount = "select count(1) from " + domain.getDomainCode();
        try(SqlRunner sqlRunner = SqlRunner.db()){
            long count = sqlRunner.selectCount(sqlCount);
            wrapper.setDomainRecordCount((int)count);
        }catch(Exception e){
            log.trace("修改模型领域字段，获取模型的记录数量异常：", e);
        }
        wrapper.setSchemaDomain(domain);
        schemaFieldServer.edit(wrapper);
        return wrapper.getRecord();
    }

    @RestfulDelete(code = "deleteSchemaField", name = "删除模型领域字段", path = "/{fieldId}")
    public void deleteSchemaField(@PathVariable("fieldId") Long fieldId) throws Exception{
        SchemaFieldDO record = schemaFieldPersistence.getById(fieldId);
        if (record == null){
            log.info("删除模型领域字段：{}，该字段不存在", fieldId);
            throw new ResultException(Errors.NO_OBJECT, "模型字段不存在");
        }
        // 如果数据表已经有记录了，则不能删除
        // 统计该模型领域的数据表的记录数量
        SchemaDomainDO domain = schemaDomainPersistence.getByCode(record.getDomainCode());
        String sqlCount = "select count(1) from " + domain.getDomainCode();
        long recordCount = 0L;
        try(SqlRunner sqlRunner = SqlRunner.db()){
            recordCount = sqlRunner.selectCount(sqlCount);
        }catch(Exception e){
            log.trace("删除模型领域字段，获取模型的记录数量异常：", e);
        }
        if (recordCount > 0){
            log.info("删除模型领域字段，本模型：{} 已经有数据记录，不能删除字段", domain.getDomainCode());
            throw new ResultException(Errors.OBJECT_REFERENCED, "模型已经有数据记录");
        }
        DeleteFieldWrapper wrapper = new DeleteFieldWrapper();
        wrapper.setRecord(record);
        wrapper.setSchemaDomain(domain);
        wrapper.setSchemaIndexes(schemaIndexPersistence.loadByDomain(record.getDomainCode()));
        schemaFieldServer.delete(wrapper);
    }

    @RestfulGet(code = "querySchemaField", name = "查询模型字段")
    public ListResultWrapper<SchemaFieldDO> querySchemaField(SchemaFieldQueryInput input,
                                                             @RequestParam(value = "pageNo", required = false, defaultValue = "1") int pageNo,
                                                             @RequestParam(value = "pageSize", required = false, defaultValue = "50") int pageSize){
        return schemaFieldServer.query(input, pageNo, pageSize);
    }

    @RestfulGet(code = "viewSchemaField", name = "查看模型字段详情", path = "/{fieldId}")
    public SchemaFieldDetail viewSchemaField(@PathVariable("fieldId") Long fieldId) throws Exception{
        SchemaFieldDO record = schemaFieldPersistence.getById(fieldId);
        if (record == null){
            log.info("查看模型领域字段详情：{}，该字段不存在", fieldId);
            throw new ResultException(Errors.NO_OBJECT, "模型字段不存在");
        }
        SchemaFieldDetail output = new SchemaFieldDetail(record);
        List<SchemaIndexDO> schemaIndexes = schemaIndexPersistence.loadByDomain(record.getDomainCode());
        for (SchemaIndexDO schemaIndex : schemaIndexes){
            List<String> columns = Arrays.asList(StringUtils.split(schemaIndex.getColumns(), ","));
            if (columns.contains(record.getJdbcFieldCode())){
                // 该索引包括了本字段
                output.addIndex(schemaIndex, columns);
            }
        }

        return output;
    }
}
